////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_CONCURRENCY_RECURSIVE_SPIN_MUTEX_INL__
#define __WN_CONCURRENCY_RECURSIVE_SPIN_MUTEX_INL__

#include "WNCore/inc/WNAtomic.h"
#include "WNConcurrency/inc/WNThread.h"

#ifdef _WN_WINDOWS
    #include <windows.h>
#elif defined _WN_LINUX
    #include <sys/types.h>
    #include <sys/syscall.h>
#endif

namespace WNConcurrency {
    WN_FORCE_INLINE WNRecursiveSpinMutex::WNRecursiveSpinMutex() :
        mSpinFlag(0),
        mRecursionCount(0),
        mOwningThread(0) {
    }

    WN_FORCE_INLINE WN_VOID WNRecursiveSpinMutex::Lock() {
        #ifdef _WN_WINDOWS
            const DWORD currentThread = GetCurrentThreadId();
        #elif defined _WN_LINUX
            const pid_t currentThread = static_cast<pid_t>(syscall(SYS_gettid));
        #endif

        if (currentThread != mOwningThread) {
            while (WNAtomicCompareSwap(&mSpinFlag, 1, 0) != 0) {
                WNThreadYield();
            }
        }

        mOwningThread = currentThread;
        mRecursionCount++;
    }

    WN_FORCE_INLINE WN_BOOL WNRecursiveSpinMutex::TryLock() {
        #ifdef _WN_WINDOWS
            const DWORD currentThread = GetCurrentThreadId();
        #elif defined _WN_LINUX
            const pid_t currentThread = static_cast<pid_t>(syscall(SYS_gettid));
        #endif

        if (mOwningThread != currentThread) {
            if (WNAtomicCompareSwap(&mSpinFlag, 1, 0) != 0) {
                return(WN_FALSE);
            }

            mOwningThread = currentThread;
        }

        mRecursionCount++;

        return(WN_TRUE);
    }

    WN_FORCE_INLINE WN_VOID WNRecursiveSpinMutex::Unlock() {
        #ifdef _WN_WINDOWS
            const DWORD currentThread = GetCurrentThreadId();
        #elif defined _WN_LINUX
            const pid_t currentThread = static_cast<pid_t>(syscall(SYS_gettid));
        #endif

        const WN_UINT32 currentRecursionCount = --mRecursionCount;

        if (currentRecursionCount == 0) {
            mOwningThread = 0;

            WNAtomicSwap(&mSpinFlag, 0);
        }
    }
}

#endif // __WN_CONCURRENCY_RECURSIVE_SPIN_MUTEX_INL__